//
//  MediaIOSettingVC.swift
//  Hermes
//
//  Created by FH on 2021/6/4.
//  Copyright © 2021 Agora. All rights reserved.
//

import Foundation
// TODO: remove
class MediaIOSetting {
    
    var title: String = ""
    var bufferType: String = "AgoraVideoBufferType"
    var pixelFormat: String = "AgoraVideoPixelFormat"
    var selected = false;
    var selectedBufferType:Int = 1
    var selectedPixelFormat:Int = 1
    
}

class ExternalAudioSource {
    var sampleRate = 44100
    var channelsPerFrame = 2
    var selected = false
}

class MediaIOSettingVC : FLViewController {
    private let appContext = HermesContext()
    private var agoraKit:AgoraRtcEngineKit?
    private var localRenderView:AgoraRenderView?
    private var remoteRenderView:AgoraRenderView?

    private var videoSourceSetting:MediaIOSetting?
    private var localRenderSetting:MediaIOSetting?
    private var remoteRenderSetting:MediaIOSetting?
    
    var externalAudioSession: AudioCaptureSessionCenter?
    var audioSetting: ExternalAudioSource?

    override func setViewStyle() {
        self.view.frame = CGRect(x: 0, y: 0, width: 800, height: 600)
    }
    
    private func dataBinding(_ needsLayout: Bool) {
        let agora = AgoraRtcEngineKit.sharedEngine(withAppId: AppAppIdType.staticCommunication.rawValue, delegate: self)
        agora.setChannelProfile(.liveBroadcasting)
        agora.setClientRole(.broadcaster)
        agora.enableVideo()
        agora.setVideoEncoderConfiguration(AgoraVideoEncoderConfiguration())
        let canvas = AgoraRtcVideoCanvas()
        canvas.view = self.localRenderView
        agora.setupLocalVideo(canvas)
        agora.startPreview()
        
        agora.joinChannel(byToken: nil, channelId: "test456",
                          info: nil, uid: 0) {[weak self, weak agora] (channel, uid, _) in
            if let self = self, let agora = agora {
                print(">>> channel:\(channel) uid: \(uid)")
//                agora.leaveChannel(nil)
//                AgoraRtcEngineKit.destroy()
            }
        }
      
        agoraKit = agora
    }
    
    private func getEnumValues(name:String) -> Dictionary<String, Int> {
        
        var node:AstNode?
        for caseNode in appContext.engine.getEnumCaseValues() {
            if caseNode.name == name {
                node = caseNode;
                break;
            }
        }
        
        var dict = Dictionary<String, Int>()
        for tmp in node!.children {
            
            if node?.declType! == "NSInteger" {
                dict[tmp.name] = Int(tmp.value?.value ?? "")!
            } else if node?.declType == "NSUInteger" {
                dict[tmp.name] = Int(tmp.value?.value ?? "")!
            }
            
        }
        
        return dict
        
    }
    
    private func getEnumValue(valueDesc:String) -> Int {
        
        var node:AstNode?
        
        for caseNode in self.appContext.engine.getEnumCaseValues() {
            if caseNode.name == valueDesc {
                node = caseNode
                break
            }
        }
        if node != nil, let itemValue = node?.value?.value {
            if node?.declType! == "NSInteger" {
                return Int(itemValue)!
            } else if node?.declType == "NSUInteger" {
                return Int(itemValue)!
            }
        }
        
        return 0
    }
    
    typealias EnumSelectBlock = (Int)->Void
    
    private func medialIOEnumTemplate(superView:NSView,
                                      width:FLValue,
                                      height:FLValue,
                                      enumName:String,
                                      selectValue:Int,
                                      title:String,
                                      block:@escaping EnumSelectBlock) {
        
        let titleLabel = FLLabel()
        titleLabel.flText = "\(title):"
        titleLabel.backgroundColor = .clear
        titleLabel.bind(to: yoga, superView) { it in
            it.width = 80
            it.height = 20
        }
        
        let popBtn = NSPopUpButton()
        
        let enums = self.getEnumValues(name: enumName)
        var emumtitles = [String]()
        for (key, value) in enums {
            emumtitles.append(key)
        }
        
        popBtn.addItems(withTitles:emumtitles)
        popBtn.selectItem(at: selectValue)
        
        popBtn.bind(to: yoga, superView) { it in
            it.width = width
            it.height = height
        }
        popBtn.rac_eventTouch.subscribeNext { btn in
            
            let value = enums[btn.titleOfSelectedItem!]
            block(value!)
            
        }
        
    }
    
    private func mediaIOTemplate(superView:NSView, setting:MediaIOSetting) {
        
        let height = FLValue(40)
        
        let bgView = NSView()
        bgView.bind(to: yoga, superView) { it in
            it.direction = .row
            it.marginVertical = 5
            it.height = height
            it.alignItems = .center
        }
        bgView.flBackgroundColor = .clear
                
        let checkBox = FLCheckbox()
        checkBox.bind(to: yoga, bgView) { it in
            it.height = height
        }
        checkBox.rac_eventTouch.subscribeNext { btn in
            if btn.state == .on {
                setting.selected = true;
            }else {
                setting.selected = false;
            }
        }
        checkBox.flTitle = setting.title + "  "
        checkBox.state = setting.selected ? .on:.off
        
        self.medialIOEnumTemplate(superView: bgView,
                                  width: FLValue(240),
                                  height: FLValue(40),
                                  enumName:setting.bufferType,
                                  selectValue: setting.selectedBufferType,
                                  title: "bufferType") { select in
            setting.selectedBufferType = select
        }
        
        self.medialIOEnumTemplate(superView: bgView,
                                  width: FLValue(210),
                                  height: FLValue(40),
                                  enumName:setting.pixelFormat,
                                  selectValue: setting.selectedPixelFormat,
                                  title: "pixelFormat") { select in
            setting.selectedPixelFormat = select
        }

    }
    
    private func medioLayout() {
        
        let videoSourceSetting = MediaIOSetting()
        videoSourceSetting.title = "Video Source"
        videoSourceSetting.selected = false
        self.mediaIOTemplate(superView: self.view, setting: videoSourceSetting)
        self.videoSourceSetting = videoSourceSetting;

        let localRenderSetting = MediaIOSetting()
        localRenderSetting.title = "Local  Render"
        localRenderSetting.selected = false
        self.mediaIOTemplate(superView: self.view, setting: localRenderSetting)
        self.localRenderSetting = localRenderSetting;
        
        let remoteRenderSetting = MediaIOSetting()
        remoteRenderSetting.title = "Remote Render"
        self.mediaIOTemplate(superView: self.view, setting: remoteRenderSetting)
        self.remoteRenderSetting = remoteRenderSetting;
        
    }
    
    private func audioExternalLayout(audioSetting:ExternalAudioSource) {
        
        let height = FLValue(40)
        
        let bgView = NSView()
        bgView.bind(to: yoga, self.view) { it in
            it.direction = .row
            it.marginVertical = 5
            it.height = height
            it.alignItems = .center
        }
        bgView.flBackgroundColor = .clear
                
        let checkBox = FLCheckbox()
        checkBox.bind(to: yoga, bgView) { it in
            it.height = height
        }
        checkBox.rac_eventTouch.subscribeNext { btn in
            if btn.state == .on {
                audioSetting.selected = true;
            }else {
                audioSetting.selected = false;
            }
        }
        checkBox.flTitle = "externalAudioSource"
        checkBox.state = .off
        
        var titleLabel = FLLabel()
        titleLabel.flText = "SampleRate:"
        titleLabel.backgroundColor = .clear
        titleLabel.bind(to: yoga, bgView) { it in
            it.width = 80
            it.height = 20
        }
        
        var input = FLInputField()
        input.flText = "44100"
        input.bind(to: yoga, bgView) { it in
            it.width = 100
            it.height = 26
            it.marginLeft = 4
        }
        input.rac_valueDidChanged.subscribeNext { text in
            audioSetting.sampleRate = Int(text) ?? 44100
        }
        
        titleLabel = FLLabel()
        titleLabel.flText = "channelsPerFrame:"
        titleLabel.backgroundColor = .clear
        titleLabel.bind(to: yoga, bgView) { it in
            it.width = 120
            it.height = 20
            it.marginLeft = 4
        }
        input = FLInputField()
        input.flText = "2"
        input.bind(to: yoga, bgView) { it in
            it.width = 100
            it.height = 26
            it.marginLeft = 4
        }
        input.rac_valueDidChanged.subscribeNext { text in
            audioSetting.channelsPerFrame = Int(text) ?? 2
        }
        
    }
    
    func layoutUI(needsLayout: Bool) {
        
        self.medioLayout()
        
        let audioSetting = ExternalAudioSource()
        self.audioExternalLayout(audioSetting:audioSetting)
        self.audioSetting = audioSetting;
        
        let btn = NSButton()
        btn.bind(to: yoga, self.view) { it in
            it.width = 800
            it.height = 44
            it.margin = 8
        }
        btn.flTitle = "conform"
        btn.flTitleColor = .black
        btn.flBackgroundColor = .white
        btn.rac_eventTouch.subscribeNext {[weak self] btn in
            if let self = self {
              
                if self.videoSourceSetting?.selected == true {
                    let customCamera = AgoraCamera()
                    customCamera.type = AgoraVideoBufferType(rawValue:self.videoSourceSetting?.selectedBufferType ?? 0)!
                    customCamera.format = AgoraVideoPixelFormat(rawValue:UInt(self.videoSourceSetting?.selectedPixelFormat ?? 0))!
                    self.agoraKit?.setVideoSource(customCamera)
                }else if self.localRenderSetting?.selected == true {
                    self.localRenderView?.type = AgoraVideoBufferType(rawValue:self.localRenderSetting?.selectedBufferType ?? 0)!
                    self.localRenderView?.format = AgoraVideoPixelFormat(rawValue:UInt(self.localRenderSetting?.selectedPixelFormat ?? 0))!
                    self.agoraKit?.setLocalVideoRenderer(self.localRenderView)
                }
                
                if self.audioSetting?.selected == true {
                    
                    print("audio.setting.sampleRate:\(self.audioSetting?.sampleRate) audio.setting.channelsPerFrame:\(self.audioSetting?.channelsPerFrame) ")
                    
                    let externalAudioSession = AudioCaptureSessionCenter(delegate: self)
                    let sampleRate = UInt(self.audioSetting!.sampleRate)
                    let numberOfChannels = UInt(self.audioSetting!.channelsPerFrame)
                    if externalAudioSession.setupCapture(sampleRate:sampleRate, numberOfChannels:numberOfChannels) == false {
//                        alert("Setup external audio falied!")
                    } else {
                        self.agoraKit?.enableExternalAudioSource(withSampleRate: sampleRate, channelsPerFrame: numberOfChannels)
                        externalAudioSession.startCapture()
                    }
                    self.externalAudioSession = externalAudioSession
                    
                }else {
                    self.agoraKit?.disableExternalAudioSource()
                }
                
            }
        }
        
        let bottomView = NSView()
        bottomView.bind(to: yoga, self.view) { it in
            it.direction = .row
            it.margin = 8
            it.flexGrow = 1
        }
        bottomView.flBackgroundColor = .clear;
        
        let localRenderView = AgoraRenderView()
        localRenderView.bind(to: yoga, bottomView) { it in
            it.margin = 4
            it.flexGrow = 1
        }
        localRenderView.flBackgroundColor = .blue
        self.localRenderView = localRenderView;
        
        let remoteRenderView = AgoraRenderView()
        remoteRenderView.bind(to: yoga, bottomView) { it in
            it.margin = 4
            it.flexGrow = 1
        }
        remoteRenderView.flBackgroundColor = .green
        self.remoteRenderView = remoteRenderView;

        yoga.apply()
        
        self.dataBinding(needsLayout)

    }
}

extension MediaIOSettingVC: AudioCaptureSessionCenterDelegate {
    func audioCaptureSessionCenter(center: AudioCaptureSessionCenter, didCaptureBuffer buffer: CMSampleBuffer) {
        self.agoraKit?.pushExternalAudioFrameSampleBuffer(buffer)
    }
}

extension MediaIOSettingVC : AgoraRtcEngineDelegate {
    
    func rtcEngine(_ engine: AgoraRtcEngineKit, didJoinedOfUid uid: UInt, elapsed: Int) {
        
        if (self.remoteRenderSetting?.selected == true) {
            self.remoteRenderView?.uid = uid
            self.remoteRenderView?.type = AgoraVideoBufferType(rawValue:self.remoteRenderSetting?.selectedBufferType ?? 0)!
            self.remoteRenderView?.format = AgoraVideoPixelFormat(rawValue:UInt(self.remoteRenderSetting?.selectedPixelFormat ?? 0))!
            self.agoraKit?.setRemoteVideoRenderer(self.remoteRenderView, forUserId: uid)
        }else {
            let canvas = AgoraRtcVideoCanvas()
            canvas.view = self.remoteRenderView
            canvas.uid = uid
            self.agoraKit?.setupRemoteVideo(canvas)
        }
        
    }
    
}
